home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d18 / opbonus.arc / ALARM.ARC / ALARMM.PAS < prev    next >
Pascal/Delphi Source File  |  1991-03-20  |  21KB  |  673 lines

  1. {$S-,R-,V-,I-,A-,F+}
  2. unit AlarmM;
  3.   {-Main unit of ALARM: A Simple PopUp Alarm Program}
  4.  
  5. interface
  6.  
  7. uses
  8.   Dos,                       {standard DOS/BIOS routines}
  9.   OpInline,                  {Object Professional Inline macros}
  10.   OpRoot,                    {Object Professional classic data structures}
  11.   OpString,                  {Object Professional string handling routines}
  12.   OpDate,                    {Object Professional Date routines}
  13.   OpDos,                     {Object Professional Dos routines}
  14.   OpCrt,                     {Object Professional CRT unit}
  15.   OpCmd,                     {Object Professional Command Processing}
  16.   OpSEdit,                   {Object Professional Simple Line Editor}
  17.   OpSwap1;                   {Object Professional TSR Swap Manager}
  18.  
  19. type
  20.   {time record}
  21.   TimeRec =
  22.     record
  23.       Hours, Minutes, Seconds : Byte;
  24.     end;
  25.   LongIntPtr = ^LongInt;
  26.  
  27.   {a type that describes our data in the code segment}
  28.   AlarmDataType =
  29.     record
  30.       AlarmTicker : LongInt;         {The tick count to popup}
  31.       DataPtr     : LongIntPtr;      {pointer to the ThisIFC.UserData field}
  32.       Int1cOn     : Boolean;         {int 1Ch installed flag}
  33.     end;
  34.   AlarmDataTypePtr = ^AlarmDataType; {pointer to CS data}
  35.  
  36. var
  37.   ShutDownProc : Procedure;
  38.   StartUpProc  : Procedure;
  39.   ScrBuf       : Pointer;
  40.   AlarmData    : AlarmDataTypePtr;   {pointer to CS data set by ALARM.PAS}
  41.   OrigInt1C    : Pointer;            {save original Int 1Ch vector}
  42.   LE           : SimpleLineEditor;   {for prompt in hotkey popup}
  43.   popTime      : TimeRec;            {TimeRec for when Alarm is to popup}
  44.  
  45. const
  46.   {colors for user interface window and prompts}
  47.   ScreenColors : ColorSet = (
  48.     TextColor       : $1B; TextMono       : $1B;
  49.     CtrlColor       : $1B; CtrlMono       : $1B;
  50.     FrameColor      : $1A; FrameMono      : $1A;
  51.     HeaderColor     : $21; HeaderMono     : $70;
  52.     ShadowColor     : $00; ShadowMono     : $00;
  53.     HighlightColor  : $00; HighlightMono  : $00;
  54.     PromptColor     : $1F; PromptMono     : $0F;
  55.     SelPromptColor  : $1F; SelPromptMono  : $0F;
  56.     ProPromptColor  : $1F; ProPromptMono  : $0F;
  57.     FieldColor      : $3B; FieldMono      : $70;
  58.     SelFieldColor   : $1B; SelFieldMono   : $07;
  59.     ProFieldColor   : $1B; ProFieldMono   : $07
  60.   );
  61.  
  62. procedure InitAlarm;
  63. {-Called from Alarm.Pas to initialize Alarm}
  64.  
  65. implementation
  66.  
  67. const
  68.   {** keep the following together to allow easy patching **}
  69.   ModuleName : string[8] = 'Alarm1.0'; {module name for standard interface}
  70.  
  71.   OurHotKey : Word = $051E;  {Ctrl + RightShift, 'A'}
  72.  
  73.   SwapPathName : String[64] = 'C:\';
  74.   SwapPath1 : String[64] = 'ALARMSW1.$$$';
  75.   SwapPath2 : String[64] = 'ALARMSW2.$$$';
  76.   SwappingOn : Boolean = True;
  77.   {******************* end of patch area ******************}
  78.  
  79.   ExtraParas : Word = (2*1024) div 16;  {2k extra on the heap}
  80.   TimeMask   = 'hh:mmt';
  81.   TimeLabel  = 'Current time';
  82.   AlarmLabel = 'Alarm time';
  83. type
  84.   String10 = string[10];
  85.   String80 = string[80];
  86.  
  87.  
  88. const
  89.   UIX1             = 20;
  90.   UIY1             = 8;
  91.   UIX2             = 60;
  92.   UIY2             = 15;
  93.   AlarmX           = UIX2 - 11;
  94.   ALarmY           = UIY1 + 2;
  95.   TimeX            = UIX1 + 2;
  96.   TimeY            = UIY1 + 2;
  97.   TimePromptX      = UIX1 + 2;
  98.   TimePromptY      = UIY1 + 4;
  99.   MsgPromptX       = TimePromptX;
  100.   MsgPromptY       = TimePromptY + 1;
  101.  
  102.   TimePrompt       = 'Enter alarm time   : ';
  103.   MsgPrompt        = 'Enter alarm message: ';
  104.   MsgLen           = 15;
  105.  
  106.   BufferSize       = (UIX2 - UIX1) * (UIY2 - UIY1) * SizeOf(Word);
  107.   MaxAlarmMsgLen   = (UIX2 - UIX1) - 2;
  108.  
  109.   {screen messages}
  110.   ProgName  : string[36] = 'Alarm: A Simple PopUp Alarm Program';
  111.   Copyright : string[22] = 'by TurboPower Software';
  112.   LoadError : string[23] = 'Unable to install Alarm';
  113.   AlarmMsg  : string[MaxAlarmMsgLen] = 'ALARM';
  114.   DisableOurselves : Boolean = False; {if true, disable the TSR}
  115.   AlarmAttr = $70;
  116.  
  117. var
  118.   NormalAttr, HeaderAttr, FrameAttr : Byte;
  119.  
  120.   procedure Tone(Freq,Duration : Word);
  121.  
  122.   begin
  123.     Sound(Freq);
  124.     Delay(Duration);
  125.     NoSound;
  126.   end;
  127.  
  128.   procedure Beep;
  129.   begin
  130.     Tone(880,100);
  131.     Tone(110,200);
  132.     Tone(440,100);
  133.   end;
  134.  
  135.   procedure GetCurrentTime(var TR : TimeRec);
  136.   {-Mystic assembly language routine to calculate current time fast.
  137.     Based on routine by Bob Tolz.}
  138.   begin
  139.     inline(
  140.       $B8/$40/$00/           {mov  ax,$40      ;read time from BIOS data area}
  141.       $8E/$C0/               {mov  es,ax       ;INT $1A clears midnight flag!}
  142.       $26/$8B/$0E/>$6E/      {mov  cx,es:[$6E]}
  143.       $26/$8B/$16/>$6C/      {mov  dx,es:[$6C]}
  144.       $89/$C8/               {mov ax,cx        ;magically calculate the time}
  145.       $89/$D3/               {mov bx,dx}
  146.       $D1/$E2/               {shl dx,1}
  147.       $D1/$D1/               {rcl cx,1}
  148.       $D1/$E2/               {shl dx,1}
  149.       $D1/$D1/               {rcl cx,1}
  150.       $01/$DA/               {add dx,bx}
  151.       $11/$C8/               {adc ax,cx}
  152.       $92/                   {xchg dx,ax}
  153.       $B9/$0B/$E9/           {mov cx,$E90B}
  154.       $F7/$F1/               {div cx}
  155.       $89/$C3/               {mov bx,ax}
  156.       $31/$C0/               {xor ax,ax}
  157.       $F7/$F1/               {div cx}
  158.       $89/$DA/               {mov dx,bx}
  159.       $B9/$C8/$00/           {mov cx,200}
  160.       $F7/$F1/               {div cx}
  161.       $80/$FA/$64/           {cmp dl,100}
  162.       $72/$03/               {jb Under}
  163.       $80/$EA/$64/           {sub dl,100}
  164.       {Under:}
  165.       $F5/                   {cmc}
  166.       $88/$D3/               {mov bl,dl}
  167.       $D1/$D0/               {rcl ax,1}
  168.       $B2/$00/               {mov dl,0}
  169.       $D1/$D2/               {rcl dx,1}
  170.       $B9/$3C/$00/           {mov cx,60}
  171.       $F7/$F1/               {div cx}
  172.       $88/$D7/               {mov bh,dl}
  173.       $F6/$F1/               {div cl}
  174.       $86/$E0/               {xchg al,ah}
  175.       $C4/$7E/<TR/           {les di,[bp+<TR]  ;ES:DI => time rec}
  176.       $26/$88/$25/           {mov es:[di],ah   ;AH has hours}
  177.       $26/$88/$45/$01/       {mov es:[di+1],al ;AL has minutes}
  178.       $26/$88/$7D/$02);      {mov es:[di+2],bh ;BH has seconds (hundredths in BL)}
  179.   end;
  180.  
  181.  
  182.   function TimeToTicks(H,M,S,S100 : Byte) : LongInt;
  183.  
  184.   const
  185.     TicsPerHr = 65543.3333;
  186.     TicsPerMin = 1092.3889;
  187.     TicsPerSec = 18.2065;
  188.     TicsPerHun = 0.182065;
  189.  
  190.   begin
  191.     TimetoTicks := Trunc((H*TicsPerHr)+(M*TicsPerMin)+
  192.                          (S*TicsPerSec)+(S100*TicsPerHun));
  193.   end;
  194.  
  195.   function TimeRecToTicks(TR : TimeRec) : LongInt;
  196.   begin
  197.     with TR do
  198.       TimeRecToTicks := TimeToTicks(Hours, Minutes, Seconds, 0);
  199.   end;
  200.  
  201.   procedure SetBiosClock;
  202.     {-Set BIOS clock to match DOS's.}
  203.   var
  204.     BiosClock : LongInt absolute $40 : $6C;
  205.     Regs : Registers;
  206.   begin
  207.     with Regs do begin
  208.       AH := $2C;
  209.       MsDos(Regs);
  210.       BiosClock := TimetoTicks(CH,CL,DH,DL);
  211.     end;
  212.   end;
  213.  
  214.   function ParseTime(TimeS : String) : Boolean;
  215.   var
  216.     H, M, S : Integer;
  217.   begin
  218.     ParseTime := False;
  219.     if Pos(':',TimeS) = 2 then
  220.       TimeS := '0' + TimeS;
  221.     if Length(TimeS) < Length(TimeMask) then begin
  222.       if not Str2Int(Copy(TimeS, 1, 2), H) then
  223.         Exit;
  224.       if H > 12 then
  225.         TimeS := TimeS + 'p'
  226.       else
  227.         TimeS := TimeS + 'a';
  228.     end;
  229.     if TimeStringToHMS(TimeMask,TimeS,H,M,S) then begin
  230.       ParseTime := True;
  231.       with popTime do begin
  232.         Hours     := Byte(H);
  233.         Minutes   := Byte(M);
  234.         Seconds   := Byte(S);
  235.       end;
  236.     end;
  237.   end;
  238.  
  239.   procedure UpdateTime;
  240.   var
  241.     TR : TimeRec;
  242.     A  : Byte;
  243.   begin
  244.     GetCurrentTime(TR);
  245.     with TR do
  246.       FastWrite(TimeToTimeString(TimeMask, HMSToTime(Hours, Minutes, Seconds)),
  247.                 TimeY, TimeX, HeaderAttr);
  248.     with popTime do
  249.       if Hours <> $FF then
  250.         FastWrite(TimeToTimeString(TimeMask,
  251.                   HMSToTime(Hours, Minutes, Seconds)),
  252.                   AlarmY, AlarmX, HeaderAttr)
  253.       else
  254.         FastWrite('<none>',AlarmY, AlarmX, FrameAttr);
  255.   end;
  256.  
  257.   function GetKey : Word;
  258.     {-Update the screen while waiting for a keystroke}
  259.   begin
  260.     while not KeyPressed do begin
  261.       {make sure other TSRs can pop up}
  262.       inline($CD/$28);
  263.       UpdateTime;
  264.     end;
  265.     GetKey := ReadKeyWord;
  266.   end;
  267.  
  268.   procedure SetAlarmTime(TR : TimeRec);
  269.   begin
  270.     AlarmData^.AlarmTicker := TimeRecToTicks(TR);
  271.   end;
  272.  
  273.   procedure UserInterface;
  274.   var
  275.     TR : TimeRec;
  276.     A,B  : Byte;
  277.     UseMono : Boolean;
  278.     S : String[MaxAlarmMsgLen];
  279.   begin
  280.     case CurrentMode of
  281.       2, 7 : UseMono := True;
  282.       else UseMono := False;
  283.     end;
  284.     if UseMono then begin
  285.       FrameAttr  := ScreenColors.FrameMono;
  286.       HeaderAttr := ScreenColors.HeaderMono;
  287.       NormalAttr := ScreenColors.TextMono;
  288.     end
  289.     else begin
  290.       FrameAttr  := ScreenColors.FrameColor;
  291.       HeaderAttr := ScreenColors.HeaderColor;
  292.       NormalAttr := ScreenColors.TextColor;
  293.     end;
  294.     ClearWindow(UIX1, UIY1, UIX2, UIY2, ' ',NormalAttr);
  295.     FrameWindow(UIX1, UIY1, UIX2, UIY2, FrameAttr, HeaderAttr, 'ALARM');
  296.     FastWrite(TimeLabel, TimeY-1, TimeX, NormalAttr);
  297.     FastWrite(AlarmLabel, AlarmY-1, AlarmX, NormalAttr);
  298.  
  299.     {prompt for alarm time}
  300.     with LE do begin
  301.       S := '';
  302.       ReadString(TimePrompt, TimePromptY, TimePromptX, 6, 6, S);
  303.       if Length(S) = 0 then
  304.         Exit;
  305.       if (GetLastCommand <> ccQuit) then begin
  306.  
  307.         {if valid time, prompt for alarm string}
  308.         if ParseTime(S) then begin
  309.           S := '';
  310.           ReadString(MsgPrompt, MsgPromptY, MsgPromptX,
  311.                      MaxAlarmMsgLen, MsgLen, S);
  312.           if GetLastCommand <> ccQuit then
  313.             AlarmMsg := S;
  314.           SetAlarmTime(popTime);
  315.         end
  316.         else begin
  317.           FastWrite('Invalid time <press any key>', MsgPromptY, MsgPromptX,
  318.                     NormalAttr);
  319.           Tone(110, 800);
  320.           if ReadKey = #0 then ;
  321.         end;
  322.       end;
  323.     end;
  324.   end;
  325.  
  326.   procedure PopupEntryPoint;
  327.     {-This is the entry point for the popup}
  328.   var
  329.     SaveXY, SaveSL : Word;   {for saving cursor position and shape}
  330.     ScrWidth,ScrHeight : Word;
  331.   begin
  332.     {reinitialize CRT}
  333.     ReInitCrt;
  334.     {exit if not in 80-column text mode}
  335.     if InTextMode and (ScreenWidth >= UIX2) then begin
  336.       {initialize screen stuff}
  337.       GetCursorState(SaveXY, SaveSL);
  338.       HiddenCursor;
  339.       if not SaveWindow(UIX1,UIY1,UIX2,UIY2,False,ScrBuf) then begin
  340.         Tone(110,500);
  341.         Exit;
  342.       end;
  343.  
  344.       UserInterface;
  345.  
  346.       {restore cursor and screen}
  347.       RestoreCursorState(SaveXY, SaveSL);
  348.       RestoreWindow(UIX1,UIY1,UIX2,UIY2,False,ScrBuf);
  349.     end
  350.     else
  351.       Tone(110,800);
  352.   end;
  353.  
  354.   procedure EntryPoint;
  355.   var
  356.     WindowDisplayed : Boolean;
  357.     P : Pointer;
  358.     CurInt1C : Pointer;                               {!!.03}
  359.   begin
  360.     {on entry into this popup routine, UserData will contain zero if this
  361.     popup is being called to display the alarm, and one if it is an unload
  362.     request}
  363.     if LongInt(CSSwapData^.ThisIFC.UserData) <> 0 then begin
  364.       if not CSSwapData^.SwapEnabled then begin       {!!.03}
  365.         GetIntVec($1C, CurInt1C);                     {!!.03}
  366.         SetIntVec($1C, OrigInt1C);                    {!!.03}
  367.       end;                                            {!!.03}
  368.       if DisableTSR then begin
  369.         {the following code needs a little explaining:
  370.          The swap system keeps a table of the entire interrupt vector table.
  371.          When a swappable TSR goes resident, a snapshot of the vector table is
  372.          taken, and all vectors (except those needed by the TSR manager) are
  373.          restored. When the popup is envoked, the contents of the saved vector
  374.          table are swapped with the current contents of the physical ISR
  375.          table. Therefore, in order to "undo" the int 1Ch handler in use by
  376.          this program, we need to poke the int 1Ch handler that was in use
  377.          before we took it over into the saved table, so the original vector
  378.          will be restored when the popup pops down. Vectors that are taken
  379.          over by OpSWAP explicitly do not require this step. The undocumented
  380.          routine SetVecOnReturn is used to poke the vector into the table.
  381.         }
  382.         SetVecOnReturn($1C, OrigInt1C);
  383.         LongInt(CSSwapData^.ThisIFC.UserData) := 1 {inform caller we succeeded}
  384.       end
  385.       else begin                                   {!!.03}
  386.         if not CSSwapData^.SwapEnabled then        {!!.03}
  387.           SetIntVec($1C, CurInt1C);                {!!.03}
  388.         LongInt(CSSwapData^.ThisIFC.UserData) := 0;{inform caller we failed}
  389.       end;                                         {!!.03}
  390.       Exit;
  391.     end;
  392.  
  393.     ReinitCrt;
  394.     if InTextMode then
  395.       WindowDisplayed := SaveWindow(1, 1, ScreenWidth, 2, True, P)
  396.     else
  397.       WindowDisplayed := False;
  398.  
  399.     if WindowDisplayed then begin
  400.       FastWrite(Center(AlarmMsg, ScreenWidth), 1, 1, NormalAttr);
  401.       FastWrite(Center('<press any key to clear>', ScreenWidth),
  402.                 2, 1, NormalAttr);
  403.     end;
  404.     repeat
  405.       Beep;
  406.       Delay(500);
  407.     until KeyPressed;
  408.     if ReadKey = #0 then ; {clear the keyboard buffer}
  409.     if WindowDisplayed then
  410.       RestoreWindow(1, 1, ScreenWidth, 2, True, P);
  411.   end;
  412.  
  413.   {$F-}
  414.  
  415.   procedure Abort(Message : string);
  416.     {-Display Message and Halt with error code}
  417.   begin
  418.     WriteLn(Message);
  419.     Halt(1);
  420.   end;
  421.  
  422.   procedure Warning(Message : String);
  423.     {-Display warning message, wait for keypress, if key is ESC, then Abort}
  424.   var
  425.     C : Char;
  426.     X,Y : Byte;
  427.   begin
  428.     WriteLn('WARNING: ',Message);
  429.     WriteLn;
  430.     X := WhereX;
  431.     Y := WhereY;
  432.     Write('Press any key to continue (ESC to abort)...');
  433.     C := ReadKey;
  434.     GotoXY(X,Y);
  435.     ClrEOL;
  436.     if C = ^[ then
  437.       Abort('Aborting at user''s request...');
  438.   end;
  439.  
  440.   procedure DisableYourself;
  441.     {-Unload resident copy of Alarm (if possible) and report results}
  442.   var
  443.     IFC : IfcPtr;
  444.     Save : Boolean;
  445.  
  446.   begin
  447.     ShutDownProc;
  448.     RestoreAllVectors;
  449.     IFC := ModulePtrByName(ModuleName);   {get the IFCPtr for this module}
  450.     if IFC <> NIL then begin              {make sure it is already installed}
  451.       Save := IFC^.CSDataPtr^.SwapMsgOn;  {save state of swap messages}
  452.       IFC^.CSDataPtr^.SwapMsgOn := False; {disable swap messages}
  453.       Write('Attempting to unload Alarm...');
  454.       LongInt(IFC^.UserData) := 1;
  455.       IFC^.CmdEntryPtr;                   {call the CmdEntryPtr}
  456.       WriteLn(^M^J);
  457.       if LongInt(IFC^.UserData) = 1 then  {check status of Unload attempt}
  458.         WriteLn('Alarm unloaded')
  459.       else
  460.         WriteLn('Unable to unload Alarm');
  461.       IFC^.CSDataPtr^.SwapMsgOn := Save;  {restore state of swap messages}
  462.     end
  463.     else
  464.       WriteLn('Alarm not installed, so it can not be unloaded!');
  465.     Halt;
  466.   end;
  467.  
  468.   procedure ShowHelp;
  469.     {-Displays help message with Alarm options}
  470.   begin
  471.     WriteLn(^M^J'Usage: Alarm [Options]'^M^J);
  472.     WriteLn('Options are:');
  473.     WriteLn('  /U          unload Alarm from memory');
  474.     WriteLn('  /N          indicates not to use swapping');
  475.     WriteLn('  /E          indicates not to use EMS');
  476.     WriteLn('  /M          indicates to squelch swapping messages');
  477.     WriteLn('  /Ppathname  specifies pathname to use for swapping');
  478.     WriteLn('  /?          displays this help screen');
  479.     Halt(0);
  480.   end;
  481.  
  482.   procedure ParseCommandLine;
  483.   var
  484.     I : Word;
  485.     Opt : String;
  486.  
  487.     procedure InvalidOption;
  488.     begin
  489.       WriteLn(Opt,' is an invalid option');
  490.       ShowHelp;
  491.     end;
  492.  
  493.   begin
  494.     for I := 1 to ParamCount do begin
  495.       Opt := ParamStr(I);
  496.       if (Opt[1] in ['/','-']) and (Length(Opt) >= 2) then begin
  497.         case UpCase(Opt[2]) of
  498.           'U' : DisableYourself;
  499.           'N' : SwappingOn := False;
  500.           'E' : SwapUseEMS := False;
  501.           'M' : SetSwapMsgOn(False);
  502.           'P' : begin
  503.                   SwapPathName := StUpcase(Copy(Opt,3,Length(Opt)));
  504.                   if SwapPathName[Length(SwapPathName)] <> '\' then
  505.                     SwapPathName := SwapPathName + '\';
  506.                 end;
  507.           '?' : ShowHelp;
  508.           else
  509.             InvalidOption;
  510.         end;
  511.       end
  512.       else
  513.         if ParseTime(Opt) then begin
  514.           SetAlarmTime(popTime);
  515.           if ParamCount >= Succ(I) then begin
  516.             AlarmMsg := ParamStr(Succ(I));
  517.             Exit;
  518.           end;
  519.         end
  520.         else
  521.           Abort(Opt+' is an invalid time');
  522.     end;
  523.   end;
  524.  
  525.   function DriveIsFixed(Drive : Char) : Boolean;
  526.     {-Return true if drive is not removable}
  527.   var
  528.     SubDrive : Char;
  529.  
  530.   begin
  531.     case GetDiskClass(Drive,SubDrive) of
  532.       Floppy360,Floppy720,
  533.       Floppy12,Floppy144,
  534.       OtherFloppy       : DriveIsFixed := False;
  535.       else DriveIsFixed := True;
  536.     end;
  537.   end;
  538.  
  539.   function PathIsValidFixedDisk(Path : String) : Boolean;
  540.     {-Return true if drive specified by Path is a valid fixed disk}
  541.   var
  542.     Drive : Char;
  543.     F : File;
  544.   begin
  545.     Assign(F,SwapPathName+SwapPath1);
  546.     Rewrite(F,1);
  547.     if IoResult <> 0 then
  548.       Abort('Cannot create swap file - Invalid path or drive not ready');
  549.     Close(F);
  550.     if IoResult <> 0 then
  551.       Abort('Error closing swap file');
  552.     if Path[2] = ':' then
  553.       Drive := UpCase(Path[1])
  554.     else
  555.       Drive := DefaultDrive;
  556.     PathIsValidFixedDisk := DriveIsFixed(Drive);
  557.   end;
  558.  
  559.   function DriveNoFromPath(Path : String) : Byte;
  560.     {-Return the DOS drive number from the pathname}
  561.   begin
  562.     if Path[2] = ':' then
  563.       DriveNoFromPath := Ord(UpCase(Path[1])) - (Ord('A') - 1)
  564.     else
  565.       DriveNoFromPath := 0;
  566.   end;
  567.  
  568.   function EnoughDiskSpaceForSwap(Paras : Word) : Boolean;
  569.     {-Returns true if enough disk space for swap files}
  570.   begin
  571.     EnoughDiskSpaceForSwap := DiskFree(DriveNoFromPath(SwapPathName)) >=
  572.                               (SwapSize(Paras) * 2);
  573.   end;
  574.  
  575.   procedure InitAlarm;
  576.     {-Called from Alarm.Pas to initialize Alarm}
  577.   var
  578.     SwapToEms : Boolean;
  579.     Paras     : Word;
  580.  
  581.   begin
  582.     FillChar(popTime, SizeOf(popTime), 0);
  583.     popTime.Hours := $FF; {this value tells ALARM that no alarm time is set}
  584.  
  585.     {signon message}
  586.     HighVideo;
  587.     WriteLn(ProgName, ^M^J, Copyright, ^M^J);
  588.     LowVideo;
  589.  
  590.     ParseCommandLine;
  591.  
  592.     if not GetMemCheck(ScrBuf, BufferSize) then
  593.       Abort('Not enough heap memory for screen buffer');
  594.  
  595.     {set up alternate GetKey routine for OPSEDIT}
  596.     SimpEditCommands.SetGetKeyProc(GetKey);
  597.     if not LE.Init(ScreenColors) then
  598.       Abort('Unable to initialize line editor (out of memory)');
  599.  
  600.     Paras := ParagraphsToKeep+ExtraParas;
  601.     SwapToEms := WillSwapUseEms(Paras);
  602.  
  603.     {if not using EMS, then check for valid fixed disk and sufficient space}
  604.     if (not SwapToEms) then begin
  605.       {check to make sure the swap path refers to a valid FIXED disk}
  606.       if (not PathIsValidFixedDisk(SwapPathName)) then
  607.         Warning('The selected swap path refers to a removable drive!');
  608.       {check for sufficient disk space for swap files}
  609.       if not EnoughDiskSpaceForSwap(Paras) then
  610.         Abort('Insufficient disk space for swap files on '+SwapPathName);
  611.     end;
  612.     {check to see if we're already installed}
  613.     if ModuleInstalled(ModuleName) then
  614.       Abort('Alarm is already loaded. Aborting...');
  615.  
  616.     {install the module}
  617.     InstallModule(ModuleName, EntryPoint);
  618.  
  619.     {check to see if SideKick is loaded}
  620.     if SideKickLoaded then
  621.       Abort('Can''t be loaded after SideKick!');
  622.  
  623.  
  624.     SetBiosClock;     {set the Bios clock = DOS Time of Day}
  625.  
  626.     {tell the CS relative ISR handler how to access the UserData field}
  627.     AlarmData^.DataPtr := @CSSwapData^.ThisIFC.UserData;
  628.  
  629.     {save the current int 1Ch vector in case user asks to unload the TSR}
  630.     GetIntVec($1C, OrigInt1C);
  631.  
  632.     {call the procedure to set up the int 1Ch handler (and exitproc)}
  633.     StartUpProc;
  634.  
  635.     {go resident}
  636.     if DefinePop(OurHotKey, PopupEntryPoint, Ptr(SSeg, SPtr)) then begin
  637.       WriteLn('Alarm loaded. Press Ctrl-RightShift-A to activate.');
  638.  
  639.       {Enable popups}
  640.       PopupsOn;
  641.       if SwappingOn then begin
  642.         if SwapToEms then begin
  643.           WriteLn('Using EMS memory for swap');
  644.           SetSwapMsgOn(False);
  645.         end
  646.         else begin
  647.           WriteLn('Swapping to ',SwapPathName + SwapPath1);
  648.           SetSwapMsgOn(True);
  649.         end;
  650.       end
  651.       else
  652.         WriteLn('Swapping disabled');
  653.  
  654.       case CurrentDisplay of
  655.         MCGA,EGA,VGA : SetSwapMsgRow($FF);
  656.       end;
  657.  
  658.  
  659.       {terminate and stay resident}
  660.       StayResSwap(ParagraphsToKeep+ExtraParas,
  661.                   0,
  662.                   SwapPathName + SwapPath1,
  663.                   SwapPathName + SwapPath2,
  664.                   SwappingOn);
  665.     end;
  666.  
  667.     {if we get here we failed}
  668.     Abort(LoadError);
  669.   end;
  670.  
  671.  
  672. end.
  673.